﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using System.Windows;
using Rxx.Labs.Properties;

namespace Rxx.Labs.Reactive.Networking
{
	[DisplayName("Web Client")]
	[Description("Making web requests using ObservableWebClient and displaying progress.")]
	public partial class WebClientLab : BaseLab
	{
		public static readonly DependencyProperty IsProgressUnknownProperty = DependencyProperty.Register(
			"IsProgressUnknown",
			typeof(bool),
			typeof(WebClientLab),
			new PropertyMetadata(true));

		public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register(
			"ProgressValue",
			typeof(int),
			typeof(WebClientLab),
			new PropertyMetadata(0));

		public static readonly DependencyProperty BytesReceivedProperty = DependencyProperty.Register(
			"BytesReceived",
			typeof(long),
			typeof(WebClientLab),
			new PropertyMetadata(0L));

		public static readonly DependencyProperty TotalBytesToReceiveProperty = DependencyProperty.Register(
			"TotalBytesToReceive",
			typeof(long),
			typeof(WebClientLab),
			new PropertyMetadata(0L));

		public static readonly DependencyProperty IsDownloadingProperty = DependencyProperty.Register(
			"IsDownloading",
			typeof(bool),
			typeof(WebClientLab),
			new PropertyMetadata(false));

		public bool IsProgressUnknown
		{
			get
			{
				return (bool) GetValue(IsProgressUnknownProperty);
			}
			private set
			{
				SetValue(IsProgressUnknownProperty, value);
			}
		}

		public int ProgressValue
		{
			get
			{
				return (int) GetValue(ProgressValueProperty);
			}
			private set
			{
				SetValue(ProgressValueProperty, value);
			}
		}

		public long BytesReceived
		{
			get
			{
				return (long) GetValue(BytesReceivedProperty);
			}
			private set
			{
				SetValue(BytesReceivedProperty, value);
			}
		}

		public long TotalBytesToReceive
		{
			get
			{
				return (long) GetValue(TotalBytesToReceiveProperty);
			}
			private set
			{
				SetValue(TotalBytesToReceiveProperty, value);
			}
		}

		public bool IsDownloading
		{
			get
			{
				return (bool) GetValue(IsDownloadingProperty);
			}
			private set
			{
				SetValue(IsDownloadingProperty, value);
			}
		}

		public Uri Url
		{
			get;
			set;
		}

		private SerialDisposable subscription;

		public WebClientLab()
		{
#if WINDOWS_PHONE
			Url = new Uri("http://ecn.channel9.msdn.com/o9/ch9/8/4/2/6/8/4/NamedAndOptionalArguments_ch9.wmv");
#else
			Url = new Uri("http://ecn.channel9.msdn.com/o9/ch9/8/4/2/6/8/4/NamedAndOptionalArguments_2MB_ch9.wmv");
#endif

			InitializeComponent();
		}

		protected override IEnumerable<IDisposable> Main()
		{
			yield return subscription = new SerialDisposable();
		}

		private void Cancel(object sender, RoutedEventArgs e)
		{
			subscription.Disposable = Disposable.Empty;
		}

		/* Note About Streaming Media: 
		 * 
		 * The following approach is meant to illustrate how to track progress for large downloads of
		 * any format.
		 * 
		 * Media is the chosen kind of resource because video and audio files are typically large and 
		 * they also contain streaming information, which supports displaying progress in real time.
		 * 
		 * However, the approach used here does not support streaming media to the player.  The entire 
		 * resource must be downloaded before it will begin to play.  The MediaElement.SetSource method 
		 * is called to assign the stream after it has been fully downloaded.
		 * 
		 * This is not a recommended way to play media files.  In a real application, you can stream 
		 * media by setting the MediaElement.Source property directly to the resource's URL.
		 * 
		 * The following approach is useful for downloading files other than media or when downloading 
		 * media for persistence without playing it immediately.
		 */
		private void Download(object sender, RoutedEventArgs e)
		{
			if (Url == null)
			{
				TraceLine(Text.InvalidUrl);

				return;
			}

			var disposables = new CompositeDisposable();

			subscription.Disposable = disposables;

			bool completed = false;

			var download = ObservableWebClient.OpenReadWithProgress(Url);

			TraceLine(Text.Subscribing);

			disposables.Add(download
				.TakeLeft()
				.Finally(() =>
					{
						TraceLine(Text.Disposing);

						completed = true;
						IsDownloading = false;
					})
				.Subscribe(
					progress =>
					{
						BytesReceived = progress.BytesReceived;
						TotalBytesToReceive = progress.TotalBytesToReceive;
						IsProgressUnknown = progress.TotalBytesToReceive <= 0;
						ProgressValue = progress.ProgressPercentage;
					},
					ex => { }));

			disposables.Add(download
				.TakeRight()
				.Subscribe(
					stream =>
					{
						TraceLine(Text.ReceivedBytesFormat, stream.Length);

#if WINDOWS_PHONE
						WriteStreamToDisc(ref stream, disposables);
#endif

						mediaElement.SetSource(stream);
					},
					ConsoleOutputOnError(),
					ConsoleOutputOnCompleted()));

			if (!completed)
			{
				IsDownloading = true;
			}
		}

#if WINDOWS_PHONE
		private static void WriteStreamToDisc(ref Stream stream, CompositeDisposable disposables)
		{
			const string name = "WebClientLab_Download.bin";

			Stream file = Storage.OpenFile(name, FileMode.Create, FileAccess.ReadWrite, FileShare.None);

			disposables.Add(file);

			using (var reader = new BinaryReader(stream))
			{
				byte[] buffer;
				while ((buffer = reader.ReadBytes(1024)).Length > 0)
				{
					file.Write(buffer, 0, buffer.Length);
				}
			}

			stream = file;

			stream.Position = 0;
		}
#endif
	}
}